package com.zhu.instructions.references;

import com.zhu.instructions.base.Index16Instruction;
import com.zhu.rtda.Frame;
import com.zhu.rtda.InstanceOop;
import com.zhu.rtda.OperandStack;
import com.zhu.rtda.Slot;
import com.zhu.rtda.heap.Field;
import com.zhu.rtda.heap.InstanceKlass;
import com.zhu.rtda.heap.constant.FieldRef;
import com.zhu.rtda.heap.constant.RuntimeConstantPool;
public class GET_FIELD extends Index16Instruction {

    @Override
    public void execute(Frame frame) {
        // 1. 获取当前方法所属的类及其运行时常量池
        InstanceKlass currentClass = frame.getMethod().getClazz();
        RuntimeConstantPool cp = currentClass.getConstantPool();

        // 2. 解析 FieldRef
        FieldRef fieldRef = (FieldRef) cp.getConstant(this.index);
        Field field = fieldRef.resolvedField();

        // 3. 检查是否为静态字段（GETFIELD 只能用于实例字段）
        if (field.isStatic()) {
            throw new IncompatibleClassChangeError();
        }

        // 4. 从操作数栈弹出对象引用
        OperandStack stack = frame.getOperandStack();
        InstanceOop ref = stack.popRef();
        if (ref == null) {
            throw new NullPointerException();
        }

        // 5. 获取字段描述符、槽位 ID 和字段存储区（Slot[]）
        String descriptor = field.getDescriptor();
        int slotId = field.getSlotId();
        // 实例字段存在对象的 fields 中
        Slot[] fields = ref.getFields();

        // 6. 根据描述符读取值并压栈（与 GETSTATIC 逻辑一致，只是 source 是 fields 而非 staticVars）
        switch (descriptor.charAt(0)) {
            case 'Z': // boolean
            case 'B': // byte
            case 'C': // char
            case 'S': // short
            case 'I': // int
                stack.pushInt(fields[slotId].getNum());
                break;
            case 'F': // float
                stack.pushFloat(Float.intBitsToFloat(fields[slotId].getNum()));
                break;
            case 'J': // long (occupies two slots)
                int low = fields[slotId].getNum();
                int high = fields[slotId + 1].getNum();
                long longVal = ((long) high << 32) | (low & 0xFFFFFFFFL);
                stack.pushLong(longVal);
                break;
            case 'D': // double
                low = fields[slotId].getNum();
                high = fields[slotId + 1].getNum();
                long doubleBits = ((long) high << 32) | (low & 0xFFFFFFFFL);
                stack.pushDouble(Double.longBitsToDouble(doubleBits));
                break;
            case 'L': // reference (object)
            case '[': // reference (array)
                stack.pushRef(fields[slotId].getRef());
                break;
            default:
                throw new RuntimeException("Unsupported field type: " + descriptor);
        }
    }
}